package uno.component;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import uno.net.UnoPlayer;
import uno.utilities.URNResolver;

public class Deck extends JPanel{

	
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 6684320661191951293L;
	
	/**
	 * */
	private LinkedList<Card> cardList;

	/**
	 * */
	private BufferedImage bf;
	
	/**
	 * */
	private Object parent;
	
	/**
	 * 
	 * */
	private boolean hidden = true;
	
	/**
	 * 
	 * Variabile che gestisce il click sul mazzo. Ogni giocatore puo' cliccare una sola volta sul mazzo per turno
	 * */
	//private boolean clickable;
	// numero di carte iniziale nel mazzo, mi serve per la fase di disegno
	//private static int DECK_SIZE;
	
	/**
	 * Costruisce un oggeto Deck completo dell'intero set di carte.
	 * */
	public Deck(Object parent) {		
		initCommonValue(parent);
		buildDeck();
		//this.setBorder(BorderFactory.createLineBorder(Color.black));		
	}
	
	/**
	 * Costruisce un oggetto Deck sulla base della lista di urn
	 * */
	public Deck(ArrayList<String> urnList, Object parent) {		
		initCommonValue(parent);
		populate(urnList);	
	}

	/**
	 * Metodo che inizializza i valori iniziali comuni ai costruttori
	 * */
	private void initCommonValue(Object parent) {
		
		this.parent = parent;
		//this.clickable = false;
		cardList = new LinkedList<Card>();
		
		try {
			bf = ImageIO.read(new File("img/cards/back.png"));
		} catch (IOException e) {
			e.printStackTrace();
			System.err.println("ERRORE: immagine copri mazzo non trovata");
		}

		//DECK_SIZE = cardList.size();
		
		//NB rimosso perche' ora devono essere esplicitamente aggiunti tramite enableDeck
		//addClickListener();
		
		/*
		setPreferredSize(new Dimension(CardFactory.getCardHeight(), CardFactory.getCardWidth() + getDeckSize()));
		setSize(new Dimension(CardFactory.getCardHeight(), CardFactory.getCardWidth() + getDeckSize()));
		setMaximumSize(new Dimension(CardFactory.getCardHeight(), CardFactory.getCardWidth() + getDeckSize()));
		*/
		
		setPreferredSize(new Dimension(CardFactory.getCardWidth(), CardFactory.getCardHeight()));
		setSize(new Dimension(CardFactory.getCardWidth(), CardFactory.getCardHeight()));
		setMaximumSize(new Dimension(CardFactory.getCardWidth(), CardFactory.getCardHeight()));
		setOpaque(false);
		
	}
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#getPreferredSize()
	 */
	@Override
	public Dimension getPreferredSize() {
		//return new Dimension(CardFactory.getCardHeight(), CardFactory.getCardWidth() + DECK_SIZE);
		return new Dimension(CardFactory.getCardWidth(), CardFactory.getCardHeight());
	}

	/**
	 * Aggiunge al mazzo le carte da 1 a 9 del colore passato come parametro
	 * */
	private void addNormalCards(String color){
		//creo la carta 0
		Card card = CardFactory.createNormalCard(0, color, null);
		cardList.add(card);
		
		//creo le altre 18 carte
		for(int k = 0; k < 2; k++) {
			for(int i = 1; i < 10; i++) {
				card = CardFactory.createNormalCard(i, color, null);
				cardList.add(card);
			}
		}
	}
	
	/**
	 * Metodo che aggiunge le carte speciali definite dal tipo 
	 * passato come parametro. Le carte possono essere:
	 * +2, cambia giro, salta un turno.
	 * */
	private void addSpecialColoredCards(int type){			
		Card card = CardFactory.createSpecialCard(type, CardValue.BLUE, null);
		Card card2 = CardFactory.createSpecialCard(type, CardValue.BLUE, null);
		cardList.add(card);
		cardList.add(card2);
		
		card = CardFactory.createSpecialCard(type, CardValue.RED, null);
		card2 = CardFactory.createSpecialCard(type, CardValue.RED, null);
		cardList.add(card);
		cardList.add(card2);
		
		card = CardFactory.createSpecialCard(type, CardValue.GREEN, null);
		card2 = CardFactory.createSpecialCard(type, CardValue.GREEN, null);
		cardList.add(card);
		cardList.add(card2);
		
		card = CardFactory.createSpecialCard(type, CardValue.YELLOW, null);
		card2 = CardFactory.createSpecialCard(type, CardValue.YELLOW, null);
		cardList.add(card);
		cardList.add(card2);			
	}
	
	/**
	 * metodo che aggiunge le carte jollie quali:
	 * +4 e cambia colore.
	 * 
	 * */
	private void addJollyCards(int type){		
		Card card = CardFactory.createSpecialCard(type, CardValue.NO_COLOR, null);
		cardList.add(card);
		
		card = CardFactory.createSpecialCard(type, CardValue.NO_COLOR, null);
		cardList.add(card);
		
		card = CardFactory.createSpecialCard(type, CardValue.NO_COLOR, null);
		cardList.add(card);
		
		card = CardFactory.createSpecialCard(type, CardValue.NO_COLOR, null);
		cardList.add(card);	
	}
	
	/**
	 * riempio il mazzo con le carte passate sotto forma di urn
	 * nella lista.
	 * 
	 * */
	private void populate(ArrayList<String> urnList){		
		for(Iterator<String> i = urnList.iterator(); i.hasNext(); ) {
			Card currentCard = URNResolver.URNToCard(i.next(), null);
			addCard(currentCard);
		}
	}
	
	/**
	 * Aggiunge una qualsiasi carta al mazzo, senza effettuare nessun controllo
	 * */
	public void addCard(Card card) {		
		cardList.add(card);		
	}
	
	/**
	 * 
	 * Popola il mazzo con tutte le carte
	 * */
	private void buildDeck(){		
		addNormalCards(CardValue.BLUE);
		addNormalCards(CardValue.GREEN);
		addNormalCards(CardValue.RED);
		addNormalCards(CardValue.YELLOW);
		addSpecialColoredCards(CardValue.CHANGE_FLOW);
		addSpecialColoredCards(CardValue.PLUS_TWO);
		//addSpecialColoredCards(CardValue.ONE_STOP);
		addJollyCards(CardValue.PLUS_FOUR);
		addJollyCards(CardValue.CHANGE_COLOR);	
	}	
	
	
	/**
	 * Rimuove dal mazzo e ritorna la carta all'indice corrispondente. 
	 * */
	public Card pollCard(int index){		
		// tolgo la carta dal mazzo.
		Card tmpCard = cardList.get(index);
		cardList.remove(index);
		return tmpCard;		
	}
	
	/**
	 * Elimina dal mazzo le carte da startIndex alla fine(la parte superiore) del mazzo
	 * */
	public void pollCardsFromParamToEnd(int startIndex) {
		for(int i = cardList.size() - 1; i >= startIndex; i--) {
			pollCard(i);
		}	
	}
	
	
	/**
	 * Ritorna una mano iniziale basato sul mazzo.
	 * @param owner giocatore che possiedera' la mano
	 * 
	 * @return Mano di carte formata da $numero_iniziale_carte
	 * */
	public HandOfCard getHOF(UnoPlayer owner){	
		HandOfCard hof = new HandOfCard(owner);
		Card card;
		for(int k = 0; k < HandOfCard.INITIAL_CARDS_NUMBER; k++) {
			card = this.pollCard(cardList.size() - 1);	
			card.setParent(hof);
			hof.addCard(card);			
		}		
		return hof;
	}
	
	
	/**
	 * Mescola le carte del mazzo
	 * 
	 * */
	public void shuffleCards(){	
		int position;		
		for(int k = 0; k < cardList.size(); k++){			
			position = new Random().nextInt(cardList.size());
			Card tmpCard = cardList.get(k);
			//tolgo l'ultima carta dal mazzo
			cardList.remove(k);
			// la re-inserisco, nel mazzo, alla nuova posizione
			cardList.add(position, tmpCard);			
		}		
	}
	
	
	/**
	 * ritorna il mazzo sotto forma di lista di urn.
	 * 
	 * @return mazzo sotto forma di urn
	 * */
	public ArrayList<String> toURNList(){
		ArrayList<String> urnList = new ArrayList<String>();
		for(Iterator<Card> i = cardList.iterator(); i.hasNext(); ){		
			String currentUrn = URNResolver.cardToURN(i.next());
			urnList.add(currentUrn);			
		}
		return urnList;
	}
	
	
	/**
	 * 
	 * Abilita o disabilita il click sul mazzo a livello di listener
	 * 
	 * @param flag true se si intende abilitarlo, false altrimenti
	 * */
	public void setEnableClickOnDeck(boolean flag) {
		MouseListener klArray[] = getMouseListeners();
		if(flag) {
			if(klArray.length == 0) addClickListener();
			else System.err.println("WARNING: Vi sono gia' dei listeners associati al mazzo, impossibile aggiungerne altri");
		} else {
			if(klArray.length > 0){
				for(int i = klArray.length - 1; i >= 0 ; i--)
					removeMouseListener(klArray[i]);
			} else
				System.err.println("WARNING: nessun listener associato al masso. Impossibile rimuoverne di ulteriori");	
		}
	}
	
	/**
	 * Ritorna true se il mazzo non e' mostrato all'utente
	 * */
	public boolean isHidden(){
		
		return hidden;
	}
	
	/**
	 * Ritorna true se il mazzo non e' mostrato all'utente
	 * */
	public void setHidden(boolean value){
		
		this.hidden = value;
	}
	
	/**
	 * Stampa il contenuto del mazzo, carta per carta, nello
	 * standard output
	 * */
	public void printAllDeck(){		
		for(int k = 0; k < cardList.size(); k++){			
			System.out.println(cardList.get(k));
		}		
	}
	
	/**
	 * ritorna la carta nella posizione specificata
	 * 
	 * @throws IndexOutOfBoundException
	 * */
	public Card getCard(int index) {
		return cardList.get(index);
	}
	
	/**
	 * Ritorna la dimensione del mazzo
	 * */
	public int getDeckSize() {
		return cardList.size();
	}
	
	/**
	 * @see javax.swing.JComponent#paint(java.awt.Graphics)
	 * 
	 * 
	 * disegna la carta "coperchio" e poi tante righe alternando bianco e nero
	 * in modo da simulare un mazzo di carte. Questo disegno parte
	 * dalla coordinate y = grandezza iniziale del mazzo - grandezza corrente, in modo
	 * tale, all'inizio partira' da 0, appena si toglia una carta parte da 1, poi 2, 3 e
	 * cosi via. Questo simula l'abbassarsi del mazzo al togliere le carte.
	 * E' necessario fare vari test(che ancora non ci sono)
	 */
	@Override
	public void paint(Graphics g) {		
		super.paint(g);
		if(getDeckSize() > 0) {
			
			//g.drawImage(bf, 0, DECK_SIZE - getDeckSize(), CardFactory.getCardHeight(), 100, this);
			g.drawImage(bf, 0, 0 , CardFactory.getCardWidth(), CardFactory.getCardHeight(), this);
			
			/*
			for( int k = 0; k < getDeckSize(); k+=2){
				g.setColor(Color.BLACK);
				g.drawLine(1, 100 + k + (DECK_SIZE - getDeckSize()), CardFactory.getCardHeight() - 1, 100 + k+ (DECK_SIZE - getDeckSize()));
				g.setColor(Color.WHITE);
				g.drawLine(1, 101 + k + (DECK_SIZE - getDeckSize()), CardFactory.getCardHeight() - 1, 101 + k+ (DECK_SIZE - getDeckSize()));
			}
			*/
			
		}		
		//setPreferredSize(new Dimension(600,600));
		//setMinimumSize(new Dimension(600,600));
		validate();			
	}
	
	public void pollLastCard() { cardList.pollLast(); }
	
	public void getLastCard() {
		HandOfCard hof = ((uno.Main)parent).getHof();
		Card card = cardList.pollLast();
		// aggiungo la possibilita' di cliccare sulla carta(se ho cliccato il mazzo sono il leader)
		card.addCardClickListeners();
		// dico alla carta che appartiene al giocatore corrente
		card.setParent(hof);
		// dico al giocatore corrente che la carta e' sua
		hof.addCard(card); 
	}
	
	
	private void addClickListener(){		
		addMouseListener(new MouseAdapter(){
			/* (non-Javadoc)
			 * @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
			 */
			@Override
			public void mouseClicked(MouseEvent e) {				
				super.mouseClicked(e);				
				if(getDeckSize() > 0){
					// pesco una carta dal mazzo
					getLastCard();					
					// aggiorno la visualizzazione del mazzo
					Deck dk = (Deck)e.getSource();
					dk.repaint();
					dk.revalidate();
					// NB per ora lo metto qui e sembra andare bene.. vedremo poi.
					setEnableClickOnDeck(false);
				}
				else {
					System.err.println("WARNING: Le carte nel mazzo sono finite!");
				}
			}			
		});		
	}
}
